/***************************************************************************
 *	@file 		fetion_listener.cpp
 *	@Author 	baoyu0125@gmail.com
 *	@create		2011-7
 *	@version 	1.0.0
 *	@brief		definition of functions of FetionListener, has asynchronous
 *				socket handler for asynchronous listening
 *	@modify		none
 ***************************************************************************/

#include "fetion_util.h"
#include "fetion_sip.h"
#include "fetion_listener.h"
#include "fetion_exception.h"

extern boost::mutex user_mutex;

FetionListener::FetionListener(boost::asio::io_service &io, FetionSip *sip) :
	io_service_(io), sip_(sip)
{
	clear();
//	listenString lstr("listening begin, %d %d.\n", sip_->callid, sip->callid);
//	stdout<<lstr;
	sip_->siptcp.async_read_some(
		boost::asio::buffer(msgBuf, BUF_LEN-1),
		boost::bind(
			&FetionListener::handle_sip_listen, this,
			boost::asio::placeholders::error,
			boost::asio::placeholders::bytes_transferred
		)
	);
/*	boost::asio::async_read(
		sip_->siptcp, boost::asio::buffer(msgBuf, BUF_LEN-1),
		boost::bind(
			&FetionListener::handle_sip_listen, this,
			boost::asio::placeholders::error,
			boost::asio::placeholders::bytes_transferred
		)
	);*/
}

void FetionListener::handle_sip_listen(const boost::system::error_code& error,
	std::size_t bytes_transferred)
{
	std::string::size_type pos;
	std::string tmp;
	unsigned int bodylen;
	
	try {
//		listenString lstr("listen %d bytes.\n", bytes_transferred);
//		stdout<<lstr;
		if(!error && bytes_transferred > 0) {
			while(true) {	// begin a new round of received message examination 
				msgBuf[bytes_transferred] = '\0';	// restrict the received message
				recvMsg += std::string(msgBuf);		// add new received message to buffer
				memset(msgBuf, 0, BUF_LEN);			// clean new message buffer
				// if received message buffer is empty
				if(recvMsg.empty()) {
					distribute_sipmsg();
					clear();	// clear received message buffer and sip list
					sip_->siptcp.async_read_some(	// end handler and continue listening
						boost::asio::buffer(msgBuf, BUF_LEN-1),
						boost::bind(
							&FetionListener::handle_sip_listen, this,
							boost::asio::placeholders::error,
							boost::asio::placeholders::bytes_transferred
						)
					);
					break;
				}
				pos = recvMsg.find("\r\n\r\n", 0);		// find end of sip header
				// message in buffer have at least one complete sip header
				if(pos != std::string::npos) {
					tmp = recvMsg.substr(0, pos);
					bodylen = sip_->get_sip_length(tmp);// get the body length of sip body from sip header
				}
				// if there is a sip header which has no body
				if(0 == bodylen && pos != std::string::npos) {
					tmp = recvMsg.substr(0, pos+4);	// store first sip header
					add_sip_recv(tmp);	// save sip header to received sip list
					// remove stored sip from received message buffer
					recvMsg = recvMsg.substr(pos+4, recvMsg.length()-pos-4);
					continue;	// continue next round of buffer examination
				}

				// there is incomplete sip header in received message buffer
				if(pos == std::string::npos) {
					memset(msgBuf, 0, BUF_LEN);
					sip_->siptcp.async_read_some(	// end handler and continue listening
						boost::asio::buffer(msgBuf, BUF_LEN-1),
						boost::bind(
							&FetionListener::handle_sip_listen, this,
							boost::asio::placeholders::error,
							boost::asio::placeholders::bytes_transferred
						)
					);
					break;
				}
				else {	// there is at least one complete sip header in buffer
					pos += 4;
					memset(msgBuf, 0, BUF_LEN);
					// there is only one incomplete sip in buffer
					if(recvMsg.length() - pos < bodylen) {
						sip_->siptcp.async_read_some(	// end handler and continue listening
							boost::asio::buffer(msgBuf, BUF_LEN-1),
							boost::bind(
								&FetionListener::handle_sip_listen, this,
								boost::asio::placeholders::error,
								boost::asio::placeholders::bytes_transferred
							)
						);
						break;
					}
					// there is just only one complete sip in buffer
					else if(recvMsg.length() - pos == bodylen) {
						add_sip_recv(recvMsg);	// add sip to sip list
						distribute_sipmsg();
						clear();	// clear received message buffer and sip list
						sip_->siptcp.async_read_some(	// end handler and continue listening
							boost::asio::buffer(msgBuf, BUF_LEN-1),
							boost::bind(
								&FetionListener::handle_sip_listen, this,
								boost::asio::placeholders::error,
								boost::asio::placeholders::bytes_transferred
							)
						);
						break;
					}
					// there is at least one complete sip in buffer
					else {
						tmp = recvMsg.substr(0, pos+bodylen);	// store the first sip in buffer
						add_sip_recv(tmp);	// add sip to sip list
						// remove stored sip from received message buffer
						recvMsg = recvMsg.substr(pos+bodylen, recvMsg.length()-pos-bodylen);
						// clean new message buffer and continue next round of buffer examination
						memset(msgBuf, 0, BUF_LEN);	
						continue;
					}
				}
			}
		}
		else if(error) {
			errorString estr("connection break out...\nprogram exit.\n");
			stdout<<estr;
			usleep(100);
			exit(FUNC_FAIL);
		}
		else {
			throw fetionException("receive message failed.");
		}
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_WARNING, "sip listening handler failed : %s.", ex.what());	
		distribute_sipmsg();
		clear();
		sip_->siptcp.async_read_some(
			boost::asio::buffer(msgBuf, BUF_LEN-1),
			boost::bind(
				&FetionListener::handle_sip_listen, this,
				boost::asio::placeholders::error,
				boost::asio::placeholders::bytes_transferred
			)
		);
		//exit(FUNC_FAIL);
	}
}

int FetionListener::distribute_sipmsg()
{
	int ret = FUNC_SUCC;
	SipProc *siprecv;

	try {
		for(std::vector<SipProc*>::iterator iter = sipRecvList.begin();
				iter != sipRecvList.end(); ++iter) {
			siprecv= *iter;
			if(NULL == siprecv) {
				continue;
			}
			//std::cout<<(*iter)->msg_<<std::endl;
			boost::mutex::scoped_lock lock(user_mutex);
			if(FUNC_SUCC != siprecv->process_received_sip()) {
				throw fetionException("process received sip failed.");
			}
		}
		//sipfile = fopen("listenSip.txt", "a");
/*		for(std::vector<SipProc*>::iterator iter = sipRecvList.begin();
				iter != sipRecvList.end(); ++iter) {
			if(*iter == NULL) continue;
			printf("\n%s\n-------------------------------------\n", 
				(*iter)->msg_.c_str());
		}*/
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "distribute sip message failed : %s.", ex.what());
		ret = FUNC_FAIL;
	}
	return ret;
}

void FetionListener::clear()
{
	SipProc *recv;
	memset(msgBuf, 0, BUF_LEN);
	recvMsg.clear();
	for(std::vector<SipProc*>::iterator iter = sipRecvList.begin();
			iter != sipRecvList.end(); ++iter) {
		recv = *iter;
		if(NULL != recv) {
			delete recv; recv = NULL;
		}
	}
	sipRecvList.clear();
}

void FetionListener::add_sip_recv(const std::string &msg)
{
	SipProc *siprecv = NULL;
	std::string::size_type pos;
	std::string type;

	if((pos = msg.find(" ", 0)) == std::string::npos) {
		siprecv = NULL;
	}
	else {
		type = msg.substr(0, pos);
		if(type == "I") {
			siprecv = NULL;//new SipRecvInviation(msg);
			WRITE_LOG(LOG_TRACE, "listening a inviation sip message.");
		}
		else if(type == "M") {
			siprecv = new SipProcMessage(msg);//NULL;
			WRITE_LOG(LOG_TRACE, "listening a chat message.");
		}
		else if(type == "BN") {
			siprecv = new SipProcNotify(msg);
			WRITE_LOG(LOG_TRACE, "listening a notification sip message.");
		}
		else if(type == "SIP-C/4.0" || type == "SIP-C/2.0") {
			siprecv = new SipProcSipc(msg);//NULL;
			WRITE_LOG(LOG_TRACE, "listening a sipc 4.0/2.0 message.");
		}
		else if(type == "IN") {
			siprecv = NULL;//new SipRecvIncoming(msg);
			WRITE_LOG(LOG_TRACE, "listening a incoming sip message.");
		}
		else {
			siprecv = NULL; 
			WRITE_LOG(LOG_TRACE, "listening a unknown sip message.");
		}
	}
	sipRecvList.push_back(siprecv);
}
